#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "log.h"
#include "dispatch.h"

static int32_t dispatch_shell_cmd(struct shell *pshell, int32_t event, void* object, void* context)
{
	int32_t ret = 0;
	cmd_object_t *pcmd;
	struct dispatch *pdispatch;
	
	pcmd = (cmd_object_t *)context;
    pdispatch = (struct dispatch*)object;
    
    if(strcmp(pcmd->cmd, "switch") == 0){
    	DBG(DBG_INFO, "cmd==>%s param==>%s\n", pcmd->cmd, pcmd->param[0]);
    }else if(strcmp(pcmd->cmd, "udp") == 0){
        struct network *pnetwork_udp;
        uint16_t port;

        if(pcmd->param[0] == NULL){
            DBG(DBG_ERR, "please input udp port \n");
            return 0;
        }
        port = atoi(pcmd->param[0]);
        if(port <= 0){
            DBG(DBG_ERR, "please input valid udp port\n");
            return 0;
        }
        
        pnetwork_udp = pdispatch->pnetwork_udp;
        ret = pnetwork_udp->op->udp_setup(pnetwork_udp, port);
        if(ret < 0){
            DBG(DBG_ERR, "network udp setup error\n");
            return ret;
        }

        DBG(DBG_INFO, "udp test port:%d\n", port);
    }else if(strcmp(pcmd->cmd, "tcp") == 0){
        struct network *pnetwork_tcp;
        uint16_t port;

        if(pcmd->param[0] == NULL){
            DBG(DBG_ERR, "please input tcp port \n");
            return 0;
        }
        port = atoi(pcmd->param[0]);
        if(port <= 0){
            DBG(DBG_ERR, "please input valid tcp port\n");
            return 0;
        }

        pnetwork_tcp = pdispatch->pnetwork_tcp;
        ret = pnetwork_tcp->op->tcp_setup(pnetwork_tcp, port);
        if(ret < 0){
            DBG(DBG_ERR, "network tcp setup error\n");
            return ret;
        }
        DBG(DBG_INFO, "tcp test port:%d\n", port);
    }else if(strcmp(pcmd->cmd, "uart") == 0){
        struct uart* puart;

        if(pcmd->param[0] == NULL){
            DBG(DBG_ERR, "please input uart device \n");
            return 0;
        }

        puart = pdispatch->puart;
        puart->config.dev = pcmd->param[0];
        ret = puart->op->open(puart);
        if(ret < 0){
            return ret;
        }
    }else if(strcmp(pcmd->cmd, "can") == 0){
        struct can* pcan;

        pcan = pdispatch->pcan;
        ret = pcan->op->open(pcan);
        if(ret < 0){
            return ret;
        }
    }else if(strcmp(pcmd->cmd, "i2c") == 0){
        struct i2c* pi2c;
        uint16_t value = 0;
        char dev[128] = {0};

        pi2c = pdispatch->pi2c;
        // pi2c->opened = 1;
        if(pcmd->param[0] == NULL){
            DBG(DBG_ERR, "please input I2C device \n");
            return 0;
        }
        sprintf(dev, "/dev/i2c-%d", (uint32_t)strtoul(pcmd->param[0], NULL, 0));
        ret = pi2c->op->open(pi2c, dev);
        if(ret < 0){
            return ret;
        }

        if(pi2c->opened){
            if(pcmd->param_nums < 3){
                DBG(DBG_INFO, "read <bus_num> <dev_addr> <reg>\n");
            } else if(pcmd->param_nums == 3){
                uint16_t dev_addr = (uint32_t)strtoul(pcmd->param[1], NULL, 0);
                uint16_t reg_addr = (uint32_t)strtoul(pcmd->param[2], NULL, 0);
                
                pi2c->op->read(pi2c, dev_addr, reg_addr, &value, 1);
                DBG(DBG_INFO, "read device=0x%02x reg=0x%02x value=0x%02x\n", dev_addr, reg_addr, value);
            } else if(pcmd->param_nums == 4){
                uint16_t dev_addr = (uint32_t)strtoul(pcmd->param[1], NULL, 0);
                uint16_t reg_addr = (uint32_t)strtoul(pcmd->param[2], NULL, 0);
                
                pi2c->op->read(pi2c, dev_addr, reg_addr, &value, 2);
                DBG(DBG_INFO, "read device=0x%02x reg=0x%02x value=0x%02x\n", dev_addr, reg_addr, value);
            }
        }

        ret = pi2c->op->close(pi2c);
        if(ret < 0){
            return ret;
        }
    }else if(strcmp(pcmd->cmd, "close") == 0){
        if(strcmp(pcmd->param[0], "udp") == 0){
            struct network *pnetwork_udp;

            pnetwork_udp = pdispatch->pnetwork_udp;
            ret = pnetwork_udp->op->close(pnetwork_udp);
            if(ret < 0){
                DBG(DBG_ERR, "network udp close error\n");
                return ret;
            }
        }else if(strcmp(pcmd->param[0], "tcp") == 0){
            struct network *pnetwork_tcp;

            pnetwork_tcp = pdispatch->pnetwork_tcp;
            ret = pnetwork_tcp->op->close(pnetwork_tcp);
            if(ret < 0){
                DBG(DBG_ERR, "network tcp close error\n");
                return ret;
            }
        }else if(strcmp(pcmd->param[0], "uart") == 0){
            struct uart* puart;

            puart = pdispatch->puart;
            ret = puart->op->close(puart);
            if(ret < 0){
                return ret;
            }
        }else if(strcmp(pcmd->param[0], "can") == 0){
            struct can* pcan;

            pcan = pdispatch->pcan;
            ret = pcan->op->close(pcan);
            if(ret < 0){
                return ret;
            }
        }else if(strcmp(pcmd->param[0], "i2c") == 0){
        }
    }else if(strcmp(pcmd->cmd, "send") == 0){
        struct uart* puart;
        struct can* pcan;

        puart = pdispatch->puart;
        pcan = pdispatch->pcan;

        if(puart->opened){
            ret = puart->op->write(puart, (uint8_t*)pcmd->param[0], strlen(pcmd->param[0]));
            if(ret < 0){
                DBG(DBG_ERR, "uart send failed\n");
            }
        }
        if(pcan->opened){
            ret = pcan->op->write(pcan, (uint8_t*)pcmd->param[0], strlen(pcmd->param[0]));
            if(ret < 0){
                DBG(DBG_ERR, "CAN send failed\n");
            }
        }
    }else if(strcmp(pcmd->cmd, "read") == 0){
        struct i2c* pi2c;

        pi2c = pdispatch->pi2c;
        if(pi2c->opened){

        }
    }else{
        DBG(DBG_INFO, "Not support\n");
    }
	
	return ret;
}

static int32_t dispatch_init(struct dispatch* pdispatch)
{
    int32_t ret;
    INIT_LIST_HEAD(&(pdispatch->head));
    lock_init(&(pdispatch->lock));
    lock_init(&(pdispatch->notify_lock));
    wait_init(&(pdispatch->wait));
    
    ret = pdispatch->op->create_shell(pdispatch);
    if(ret < 0){
        DBG(DBG_ERR, "create shell error\n");
        return ret;
    }

    ret = pdispatch->op->create_network(pdispatch);
    if(ret < 0){
        DBG(DBG_ERR, "create network error\n");
        return ret;
    }

    ret = pdispatch->op->create_uart(pdispatch);
    if(ret < 0){
        DBG(DBG_ERR, "create uart error\n");
        return ret;
    }

    ret = pdispatch->op->create_can(pdispatch);
    if(ret < 0){
        DBG(DBG_ERR, "create CAN error\n");
        return ret;
    }

    ret = pdispatch->op->create_i2c(pdispatch);
    if(ret < 0){
        DBG(DBG_ERR, "create i2c error\n");
        return ret;
    }

    DBG(DBG_INFO,"init dispatch ok\n");
    return 0;
}

static int32_t dispatch_release(struct dispatch* pdispatch)
{
    pdispatch->op->destroy_i2c(pdispatch);
    pdispatch->op->destroy_can(pdispatch);
    pdispatch->op->destroy_uart(pdispatch);
    pdispatch->op->destroy_network(pdispatch);
	pdispatch->op->destroy_shell(pdispatch);
	
    lock_destroy((&pdispatch->notify_lock));
    lock_destroy((&pdispatch->lock));
    wait_destroy((&pdispatch->wait));
    return 0;
}

static int32_t dispatch_start(struct dispatch* pdispatch)
{
    int32_t ret = 0;
#if 0
    struct network *pnetwork_udp;
    struct network *pnetwork_tcp;
    
    pnetwork_udp = pdispatch->pnetwork_udp;
    ret = pnetwork_udp->op->udp_setup(pnetwork_udp);
    if(ret < 0){
        DBG(DBG_ERR, "network udp setup error\n");
        return ret;
    }

    pnetwork_tcp = pdispatch->pnetwork_tcp;
    ret = pnetwork_tcp->op->tcp_setup(pnetwork_tcp);
    if(ret < 0){
        DBG(DBG_ERR, "network tcp setup error\n");
        return ret;
    }
#endif
	return ret;
}

static int32_t dispatch_stop(struct dispatch* pdispatch)
{
    int32_t ret = 0;
    
	return ret;
}

static int32_t dispatch_create_shell(struct dispatch* pdispatch)
{
    int32_t ret = 0;
    struct shell *pshell;
    
    ret = create_init_shell(&pdispatch->pshell);
	if(ret < 0){
		DBG(DBG_ERR, "create shell error\n");
		return ret;
	}
	
	pshell = pdispatch->pshell;
	pshell->op->register_notify(pshell, SHELL_EVENT_COMMANDS, dispatch_shell_cmd, pdispatch);
	
	return ret;
}

static int32_t dispatch_destroy_shell(struct dispatch* pdispatch)
{
    int32_t ret = 0;
    struct shell *pshell;
    
    pshell = pdispatch->pshell;
	
    if(pshell){
    	pshell->op->unregister_notify(pshell, SHELL_EVENT_COMMANDS, pdispatch);
        release_destroy_shell(pshell);
        pdispatch->pshell = NULL;
	}
	
	return ret;
}

static int32_t dispatch_create_network(struct dispatch* pdispatch)
{
    int32_t ret = 0;
    
    ret = create_init_network(&pdispatch->pnetwork_udp);
	if(ret < 0){
		DBG(DBG_ERR, "create udp network error\n");
		return ret;
	}

    ret = create_init_network(&pdispatch->pnetwork_tcp);
	if(ret < 0){
		DBG(DBG_ERR, "create tcp network error\n");
		return ret;
	}
	
	return ret;
}

static int32_t dispatch_destroy_network(struct dispatch* pdispatch)
{
    int32_t ret = 0;
    struct network *pnetwork_udp;
    struct network *pnetwork_tcp;
    
    pnetwork_udp = pdispatch->pnetwork_udp;
    if(pnetwork_udp){
        release_destroy_network(pnetwork_udp);
        pdispatch->pnetwork_udp = NULL;
	}
    
    pnetwork_tcp = pdispatch->pnetwork_tcp;
    if(pnetwork_tcp){
        release_destroy_network(pnetwork_tcp);
        pdispatch->pnetwork_tcp = NULL;
	}
	
	return ret;
}

static int32_t dispatch_create_uart(struct dispatch* pdispatch)
{
    int32_t ret = 0;
    
    ret = create_init_uart(&pdispatch->puart);
	if(ret < 0){
		DBG(DBG_ERR, "create uart error\n");
		return ret;
	}
	
	return ret;
}

static int32_t dispatch_destroy_uart(struct dispatch* pdispatch)
{
    int32_t ret = 0;
    struct uart *puart;
    
    puart = pdispatch->puart;
    if(puart){
        release_destroy_uart(puart);
        pdispatch->puart = NULL;
	}
	
	return ret;
}

static int32_t dispatch_create_can(struct dispatch* pdispatch)
{
    int32_t ret = 0;
    
    ret = create_init_can(&pdispatch->pcan);
	if(ret < 0){
		DBG(DBG_ERR, "create CAN error\n");
		return ret;
	}
	
	return ret;
}

static int32_t dispatch_destroy_can(struct dispatch* pdispatch)
{
    int32_t ret = 0;
    struct can *pcan;
    
    pcan = pdispatch->pcan;
    if(pcan){
        release_destroy_can(pcan);
        pdispatch->pcan = NULL;
	}
	
	return ret;
}

static int32_t dispatch_create_i2c(struct dispatch* pdispatch)
{
    int32_t ret = 0;
    
    ret = create_init_i2c(&pdispatch->pi2c);
	if(ret < 0){
		DBG(DBG_ERR, "create i2c error\n");
		return ret;
	}
	
	return ret;
}

static int32_t dispatch_destroy_i2c(struct dispatch* pdispatch)
{
    int32_t ret = 0;
    struct i2c *pi2c;
    
    pi2c = pdispatch->pi2c;
    if(pi2c){
        release_destroy_i2c(pi2c);
        pdispatch->pi2c = NULL;
	}
	
	return ret;
}

static int32_t dispatch_register_notify(struct dispatch* pdispatch, int32_t event, dispatch_event_notify notify, void* object)
{
    struct dispatch_event_action* paction;
    if(!notify || (event <= DISPATCH_EVENT_NONE) || (event >= DISPATCH_EVENT_MAX)){
        return -EINVAL;
    }
    paction = (struct dispatch_event_action*)malloc(sizeof(struct dispatch_event_action));
    if(!paction){
        DBG(DBG_ERR,"malloc error\n");
        return -ENOMEM;
    }
    paction->notify = notify;
    paction->object = object;
    lock(&(pdispatch->notify_lock));
    paction->next = pdispatch->paction[event];
    pdispatch->paction[event] = paction;
    unlock(&(pdispatch->notify_lock));
    return 0;
}

static int32_t dispatch_unregister_notify(struct dispatch* pdispatch, int32_t event, void* object)
{
    struct dispatch_event_action *paction,* ptmp;
    if((event <= DISPATCH_EVENT_NONE) || (event >= DISPATCH_EVENT_MAX)){
        return -EINVAL;
    }
    lock(&(pdispatch->notify_lock));
    paction = pdispatch->paction[event];
    if(paction){
        if(paction->object == object){
            pdispatch->paction[event] = paction->next;
            free(paction);
        }else{
            while(paction->next){
                if(paction->next->object == object){
                    ptmp = paction->next;
                    paction->next = ptmp->next;
                    free(ptmp);
                    break;
                }
                paction = paction->next;
            }
        }
    }
    unlock(&(pdispatch->notify_lock));
    return 0;
}

static int32_t dispatch_trigger_notify(struct dispatch* pdispatch, int32_t event, void* context)
{
    struct dispatch_event_action* paction;
    if((event <= DISPATCH_EVENT_NONE) || (event >= DISPATCH_EVENT_MAX)){
        return -EINVAL;
    }
    lock(&(pdispatch->notify_lock));
    paction = pdispatch->paction[event];
    while(paction){
        paction->notify(pdispatch, event, paction->object, context);
        paction = paction->next;
    }
    unlock(&(pdispatch->notify_lock));
    return 0;
}

static struct dispatch_operation dispatch_op =
{
    .init = dispatch_init,
    .release = dispatch_release,

	.start = dispatch_start,
	.stop = dispatch_stop,
	
	.create_shell = dispatch_create_shell,
	.destroy_shell = dispatch_destroy_shell,

    .create_network = dispatch_create_network,
	.destroy_network = dispatch_destroy_network,

    .create_uart = dispatch_create_uart,
	.destroy_uart = dispatch_destroy_uart,

    .create_can = dispatch_create_can,
	.destroy_can = dispatch_destroy_can,

    .create_i2c = dispatch_create_i2c,
	.destroy_i2c = dispatch_destroy_i2c,
	
    .register_notify = dispatch_register_notify,
    .unregister_notify = dispatch_unregister_notify,
    .trigger_notify = dispatch_trigger_notify,
};

int32_t create_init_dispatch(struct dispatch** pdispatch)
{
    int32_t ret;
    struct dispatch* ptmp;
    (*pdispatch) = (struct dispatch*)malloc(sizeof(struct dispatch));
    if(!(*pdispatch)){
        DBG(DBG_ERR,"malloc error\n");
        return -ENOMEM;
    }
    ptmp = *pdispatch;
    memset(ptmp, 0, sizeof(struct dispatch));
    ptmp->op = &dispatch_op;
    ret = ptmp->op->init(ptmp);
    if(ret < 0){
        DBG(DBG_ERR,"init error\n");
        release_destroy_dispatch(ptmp);
        return ret;
    }
    return 0;
}

void release_destroy_dispatch(struct dispatch* pdispatch)
{
    if(pdispatch){
        pdispatch->op->release(pdispatch);
        free(pdispatch);
    }
}

